#include "analyzer.h"
#include "gerror.h"
#include "log.h"

using namespace std;

#define PY_ANL_MODULE "analyzer"
#define PY_ANL_OBJ "getAnalyzer"
#define PY_ACCUMULATE "accumulate"
#define PY_ASSERT "assertCoding"
#define PY_PRINTS "printStats"
#define PY_PRINTR "printRow"

Analyzer::Analyzer(Invocation& args) :
		asserting(args.asserting), benchmark(args.benchmark)
{
	PyObject *pModule, *pGetObj, *pArgs, *pValue;
	pModule = PyImport_ImportModule(PY_ANL_MODULE);
	pythonError(pModule, PYERR_MODULE(PY_ANL_MODULE));

	pGetObj = PyObject_GetAttrString(pModule, PY_ANL_OBJ);
	pythonError(pGetObj, PYERR_ATTR(PY_ANL_OBJ));

	pValue = PyBool_FromLong(asserting);
	pArgs = PyTuple_Pack(1, pValue);
	pAnalyzer = PyObject_CallObject(pGetObj, pArgs);
	pythonError(pAnalyzer, PYERR_ATTR(PY_ANL_OBJ));
	Py_DECREF(pValue);
	Py_DECREF(pArgs);

	pAccumulate = PyObject_GetAttrString(pAnalyzer, PY_ACCUMULATE);
	pythonError(pAccumulate, PYERR_ATTR(PY_ACCUMULATE));

	pAssertCoding = PyObject_GetAttrString(pAnalyzer, PY_ASSERT);
	pythonError(pAssertCoding, PYERR_ATTR(PY_ASSERT));

	pPrintStats = PyObject_GetAttrString(pAnalyzer, PY_PRINTS);
	pythonError(pPrintStats, PYERR_ATTR(PY_PRINTS));

	pPrintRow = PyObject_GetAttrString(pAnalyzer, PY_PRINTR);
	pythonError(pPrintRow, PYERR_ATTR(PY_PRINTR));

	Py_DECREF(pGetObj);
	Py_DECREF(pModule);
}

Analyzer::~Analyzer()
{
	Py_DECREF(pAnalyzer);
	Py_DECREF(pAccumulate);
	Py_DECREF(pAssertCoding);
	Py_DECREF(pPrintStats);
	Py_DECREF(pPrintRow);
}

void Analyzer::accumulate(std::string inPath, std::string outPath, int M, int W,
		bool assertion, bool adapt)
{
	if (!benchmark)
		return;

	PyObject *pInPath;
	PyObject *pOutPath;
	PyObject *pM;
	PyObject *pW;
	PyObject *pAssertion;
	PyObject *pAdapt;
	PyObject *pArgs;
	PyObject *pResult;

	pInPath = PyString_FromString(inPath.c_str());
	pOutPath = PyString_FromString(outPath.c_str());
	pM = PyInt_FromLong(M);
	pW = PyInt_FromLong(W);
	pAssertion = PyBool_FromLong(assertion);
	pAdapt = PyBool_FromLong(adapt);
	pArgs = PyTuple_Pack(6, pInPath, pOutPath, pM, pW, pAssertion, pAdapt);

	pResult = PyObject_CallObject(pAccumulate, pArgs);
	pythonError(pAccumulate, PY_ACCUMULATE);

	Py_DECREF(pResult);
	Py_DECREF(pArgs);
	Py_DECREF(pInPath);
	Py_DECREF(pOutPath);
	Py_DECREF(pM);
	Py_DECREF(pW);
	Py_DECREF(pAssertion);
	Py_DECREF(pAdapt);
}

bool Analyzer::assertCoding(std::string inPath, std::string outPath, bool del)
{
	if (!asserting)
		return false;

	logger.info("Verifying encoding with md5sum...");

	PyObject *pInPath;
	PyObject *pOutPath;
	PyObject *pDel;
	PyObject *pArgs;
	PyObject *pResult;

	pInPath = PyString_FromString(inPath.c_str());
	pOutPath = PyString_FromString(outPath.c_str());
	pDel = PyBool_FromLong(del);
	pArgs = PyTuple_Pack(3, pInPath, pOutPath, pDel);

	pResult = PyObject_CallObject(pAssertCoding, pArgs);
	pythonError(pAssertCoding, PY_ASSERT);

	bool result = PyInt_AsLong(pResult);

	Py_DECREF(pResult);
	Py_DECREF(pArgs);
	Py_DECREF(pInPath);
	Py_DECREF(pOutPath);
	Py_DECREF(pDel);

	logger.append("done\n");
	if (!result)
		logger.error("Assertion failed!");
	return result;
}

void Analyzer::printStats(bool header)
{
	if (!benchmark)
		return;

	PyObject *pHead;
	PyObject *pArgs;
	PyObject *pResult;

	pHead = PyBool_FromLong(header);
	pArgs = PyTuple_Pack(1, pHead);
	pResult = PyObject_CallObject(pPrintStats, pArgs);
	pythonError(pPrintStats, PY_PRINTS);

	Py_DECREF(pResult);
	Py_DECREF(pArgs);
	Py_DECREF(pHead);
}

void Analyzer::printRow()
{
	if (!benchmark)
		return;

	PyObject *pResult;

	pResult = PyObject_CallObject(pPrintRow, NULL);
	pythonError(pPrintRow, PY_PRINTR);

	Py_DECREF(pResult);
}
